﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using Microsoft.Practices.Unity;
using Wyn.PMS.Common.SecurityControl;
using Wyn.PMS.Common.Utils;
using Wyn.PMS.Entity.DataAccess;
using Wyn.PMS.Entity.Entities;
using Wyn.PMS.Entity.IDataAccess;

namespace Wyn.PMS.Common.NightAudit
{
    /// <summary>
    /// 营业日缓存
    /// <para>--------------------------</para>
    /// <para>@Author: cnalexi@gmail.com</para> 
    /// <para>@Create: 2012/3/30</para>  
    /// </summary>
    public class BizDayCache
    {
        private static readonly IUnityContainer container = UnityContainerService.GetContainer();

        private IDictionary<string, HotelBizDay> _cache = new Dictionary<string, HotelBizDay>();
        
        private DateTime _versionTimeStamp = DateTime.Parse("1984/11/09 18:00:00 PM");
        
        private static readonly ReaderWriterLockSlim barrier = new ReaderWriterLockSlim();

        private static volatile BizDayCache _instance;

        private static readonly object _lock = new object();

        /// <summary>
        /// Key:RoomPriceID
        /// </summary>
        private ReadonlyDictionary<string, HotelBizDay> _readonlyBizDay;

        private BizDayCache() { }

        public static BizDayCache GetInstance()
        {
            if(_instance==null)
            {
                lock(_lock)
                {
                    if(_instance==null)
                    {
                        var pc = new BizDayCache();

                        pc.InitCache();

                        _instance = pc;

                        Thread.MemoryBarrier();
                    }
                }
            }

            return _instance;
        }
        
        private void InitCache()
        {
            IHotelBizDayRepository hotelBizdayRepository = new HotelBizDayRepository();

            try
            {
                IList<HotelBizDay> bizDays = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    bizDays = hotelBizdayRepository.FindAll(GlobalConst.ROOT_HOTEL_ID);

                    scope.Complete();
                }

                if (bizDays != null && bizDays.Count > 0) FormatCache(bizDays);

            }catch(Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "营业日初始化失败" + error);
            }

        }
        
        /// <summary>
        /// 刷新线程 （hotelId=null则刷新全部）
        /// </summary>
        /// <param name="hotelId"></param>
        public void RefreshCache(string hotelId)
        {
            if(string.IsNullOrEmpty(hotelId))
            {
                RefreshCache();
                return;
            }
            IHotelBizDayRepository hotelBizdayRepository = new HotelBizDayRepository();

            Expression<Func<HotelBizDay, bool>> exp = r => r.HotelID==hotelId;

            try{

                IList<HotelBizDay> bizDays = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    bizDays = hotelBizdayRepository.BatchFind(GlobalConst.ROOT_HOTEL_ID, exp);

                    scope.Complete();
                }

                if (bizDays != null && bizDays.Count > 0) FormatCache(bizDays);

            }catch(Exception error)
            {
                OperateLog.ErrorLog(string.Format("{0}酒店营业日于{1}刷新失败:{2}", hotelId, DateTime.Now.ToString(), error));
            }
        }

        public void RefreshCache()
        {
            IHotelBizDayRepository hotelBizdayRepository = container.Resolve<IHotelBizDayRepository>();

            Expression<Func<HotelBizDay, bool>> exp = r => r.AuditTime > _versionTimeStamp;
            
            try
            {
                IList<HotelBizDay> bizDays = null;

                var transactionOptions = new System.Transactions.TransactionOptions();

                transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;

                using (var scope = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, transactionOptions))
                {
                    bizDays = hotelBizdayRepository.BatchFind(GlobalConst.ROOT_HOTEL_ID, exp);

                    scope.Complete();
                }

                if (bizDays != null && bizDays.Count > 0) FormatCache(bizDays);

            }
            catch (Exception error)
            {
                OperateLog.ErrorLog(DateTime.Now + "营业日全部刷新失败" + error);
            }
        }

        /// <summary>
        /// 获取酒店夜审营业日信息
        /// </summary>
        /// <param name="hotelId"></param>
        /// <returns></returns>
        public HotelBizDay GetHotelBizDay(string hotelId)
        {
            return _readonlyBizDay[hotelId];
        }

        /// <summary>
        /// 获取酒店列表
        /// </summary>
        /// <returns></returns>
        public IEnumerable<HotelBizDay> GetHotelList()
        {
            return _readonlyBizDay.Select(r => r.Value);
        }

        private void FormatCache(IList<HotelBizDay> bizDays)
        {
            _versionTimeStamp = bizDays.Max(r => r.AuditTime);

            foreach (var day in bizDays)
            {
                string key = day.HotelID;

                if (_cache.ContainsKey(key))
                {
                    _cache.Remove(key);
                }

                if (day.IsValid)
                {
                    _cache.Add(key, day);
                }
            }

            _readonlyBizDay = new ReadonlyDictionary<string, HotelBizDay>(_cache);
        }
    }
}
